import GraphicsLayer from "@geoscene/core/layers/GraphicsLayer";
import {getSymbol} from "./lib/symbol"
import Graphic from "@geoscene/core/Graphic";
import Point from "@geoscene/core/geometry/Point";
import PolyLine from "@geoscene/core/geometry/PolyLine";
import PictureMarkerSymbol from "@geoscene/core/symbols/PictureMarkerSymbol";
import * as geometryEngine from "@geoscene/core/geometry/geometryEngine";

import tipPic from "./resource/titleTip.png"

interface LabelPointOptions {
  pointArray: Array<number>;
  text?: string;
  iconType?: string;
  width?: number;
  height?: number;
  fontSize?: number;
  fontColor?: string;
  titleTip?: string;
}

interface PointLineOptions {
  pointArray: Array<Array<number>>;
  iconType?: string;
  width?: number;
  height?: number;
  text?: string;
  fontSize?: number;
  fontColor?: string;
  titleTip?: string;
}

export default class MixGraphicsLayer extends GraphicsLayer {
  labelPoints: any = [];
  pointLines: any = [];
  drawGraphics: any = [];
  bottomLineSymbol: any;
  dynamicLineSymbol: any;
  allCreateFunction: any = {};
  constructor(options: any) {
    console.log(options);

    super(options);
    this.id = options.id || "mixGraphicsLayer";
    this.bottomLineSymbol = options.bottomLineSymbol || {
      type: "simple-line",
      color: [0, 0, 228, 1],
      width: 2,
      style: "solid",
    };
    this.dynamicLineSymbol = options.dynamicLineSymbol || {
      type: "simple-line",
      color: [56, 248, 128, 1],
      width: "4px",
      // style: "short-dot"
      style: "solid",
    };
    this.initFun();
  }

  initFun() {
    this.allCreateFunction = {
      createLabelPoint: (options: LabelPointOptions) =>
        this.createLabelPoint(options),
      createFirePoint: (options: LabelPointOptions) =>
        this.createFirePoint(options), // 点-火
      createSmokePoint: (options: LabelPointOptions) =>
        this.createSmokePoint(options), // 点-烟
      createWindPoint: (options: LabelPointOptions) =>
        this.createWindPoint(options), // 点-风
      createWaterCoolingSystemPoint: (options: LabelPointOptions) =>
        this.createWaterCoolingSystemPoint(options), // 点-冷却水系统
      createHalfFixedWaterFacilityPoint: (options: LabelPointOptions) =>
        this.createHalfFixedWaterFacilityPoint(options), // 点-半固定水厂
      createSprayWaterSystemPoint: (options: LabelPointOptions) =>
        this.createSprayWaterSystemPoint(options), // 点-喷淋系统
      createFixedWaterCannonPoint: (options: LabelPointOptions) =>
        this.createFixedWaterCannonPoint(options), // 点-固定水炮
      createWaterSourcePoint: (options: LabelPointOptions) =>
        this.createWaterSourcePoint(options), // 点-水源
      createIndoorFireSprinklerPoint: (options: LabelPointOptions) =>
        this.createIndoorFireSprinklerPoint(options), // 点-室内喷火系统
      createOutdoorFireSprinklerPoint: (options: LabelPointOptions) =>
        this.createOutdoorFireSprinklerPoint(options), // 点-室外喷火系统
      createWaterPumpConnectorPoint: (options: LabelPointOptions) =>
        this.createWaterPumpConnectorPoint(options), // 点-水泵接合器
      createFireWaterPoolPoint: (options: LabelPointOptions) =>
        this.createFireWaterPoolPoint(options), // 点-火灾水池
      createFireWaterBoxPoint: (options: LabelPointOptions) =>
        this.createFireWaterBoxPoint(options), // 点-火灾水箱
      createFireWaterEelPoint: (options: LabelPointOptions) =>
        this.createFireWaterEelPoint(options), // 点-火灾水膜
      createFireWaterPumpRoomPoint: (options: LabelPointOptions) =>
        this.createFireWaterPumpRoomPoint(options), // 点-消防泵房
      createPointLine: (options: PointLineOptions) =>
        this.createPointLine(options), // 线-点线
      createPolicemanPoint: (options: PointLineOptions) =>
        this.createPolicemanPoint(options), // 警车-实际图
      createElevatorCarPoint: (options: PointLineOptions) =>
        this.createElevatorCarPoint(options), // 警车-虚拟图
      createDoctorPoint: (options: PointLineOptions) =>
        this.createDoctorPoint(options), // 医生-实际图
      createAmbulancePoint: (options: PointLineOptions) =>
        this.createAmbulancePoint(options), // 救护车-实际图
      createUAVPoint: (options: PointLineOptions) =>
        this.createUAVPoint(options), // 无人机-实际图
      createPolicePoint: (options: PointLineOptions) =>
        this.createPolicePoint(options), // 警察-实际图
      createFirefighterPoint: (options: PointLineOptions) =>
        this.createFirefighterPoint(options), // 消防员-实际图
      createFiretruckPoint: (options: PointLineOptions) =>
        this.createFiretruckPoint(options), // 消防车-实际图
      createAirplanePoint: (options: PointLineOptions) =>
        this.createAirplanePoint(options), // 飞机-实际图
      createShipPoint: (options: PointLineOptions) =>
        this.createShipPoint(options), // 船-实际图
      createCitizenPoint: (options: PointLineOptions) =>
        this.createCitizenPoint(options), // 市民-实际图
    };
  }

  excute(type: string, options: any) {
    if (this.allCreateFunction[type]) {
      this.allCreateFunction[type](options);
    }
  }

  textBecomeImg(
    text: string,
    fontSize: number,
    fontcolor: string,
    backWidth: number,
    backHeight: number,
    tipPic: string
  ) {
    return new Promise((resove) => {
      var canvas: any = document.createElement("canvas");
      canvas.setAttribute("width", backWidth + "");
      canvas.setAttribute("height", backHeight + "");
      let fontsize = fontSize * 2;
      //对于g j 等有时会有遮挡，这里增加一些高度
      canvas.height = backHeight * 2;
      canvas.width = backWidth * 2;
      let context: any = canvas.getContext("2d");
      // 擦除(0,0)位置大小为200x200的矩形，擦除的意思是把该区域变为透明
      context.clearRect(0, 0, canvas.width, canvas.height);
      // 绘制图像作为背景
      let image: any = document.createElement("img");
      image.setAttribute("src", tipPic);
      image.setAttribute("border", "0");
      image.setAttribute("width", backWidth * 2 + "");
      image.setAttribute("height", backHeight * 2 + "");

      image.onload = function () {
        context.drawImage(image, 0, 0, backWidth * 2, backHeight * 2);
        context.fillStyle = fontcolor;
        context.font = fontsize + "px Arial";
        //top（顶部对齐） hanging（悬挂） middle（中间对齐） bottom（底部对齐） alphabetic是默认值
        context.textBaseline = "middle";
        context.fillText(text, 10, fontsize - 4);

        var dataUrl = canvas.toDataURL("image/png"); //注意这里背景透明的话，需要使用png
        resove(dataUrl);
      };
    });
  }

  getStringLength(str: string) {
    let realLength = 0,
      len = str.length,
      charCode = -1;
    for (var i = 0; i < len; i++) {
      charCode = str.charCodeAt(i);
      if (charCode >= 0 && charCode <= 128) {
        realLength += 1;
      } else {
        realLength += 2;
      }
    }
    return realLength;
  }

  async createTextPicturePoint(options: LabelPointOptions) {
    //设置默认值
    const {
      pointArray,
      text = "",
      iconType = "点-火",
      width = 24,
      height = 24,
      fontSize = 14,
      fontColor = "white",
      titleTip = tipPic,
    } = options;

    let graphicObj: any = {
      geometry: {
        type: "point",
        longitude: pointArray[0],
        latitude: pointArray[1],
      },
    };
    let graphic: Graphic = new Graphic(graphicObj);
    let len: number = this.getStringLength(text);
    let perLen: number = fontSize;
    let iconWidth: number = width;
    let iconHeight: number = height;
    let textGraphic: Graphic = new Graphic(graphicObj);
    let padding: number = 8;
    let backWidth: number = (len * perLen) / 2 + padding;
    let backHeight: number = perLen + padding;
    console.log(graphic);

    let picture: any = await this.textBecomeImg(
      text,
      fontSize,
      fontColor,
      backWidth,
      backHeight,
      titleTip
    );
    textGraphic.symbol = new PictureMarkerSymbol({
      url: picture,
      yoffset: iconHeight + backHeight / 2 + 5 + "px",
      // xoffset: '3px',
      width: backWidth + "pt",
      height: backHeight + "pt",
    });
    graphic.symbol = getSymbol(
      iconType,
      iconWidth,
      iconHeight,
      0,
      iconHeight / 2
    );
    return { graphic, textGraphic };
  }

  async createLabelPoint(options: LabelPointOptions) {
    let { graphic, textGraphic } = await this.createTextPicturePoint(options);
    this.addMany([graphic, textGraphic]);

    this.labelPoints.push({
      type: "label-point",
      graphic: graphic,
      textGraphic: textGraphic,
    });
  }
  // 点-火
  createFirePoint(options: LabelPointOptions) {
    options.iconType = "点-火";
    this.createLabelPoint(options);
  }
  // 点-烟
  createSmokePoint(options: LabelPointOptions) {
    options.iconType = "点-烟";
    this.createLabelPoint(options);
  }
  // 点-风
  createWindPoint(options: LabelPointOptions) {
    options.iconType = "点-风";
    this.createLabelPoint(options);
  }

  // 点-冷却水系统
  createWaterCoolingSystemPoint(options: LabelPointOptions) {
    options.iconType = "点-冷却水系统";
    this.createLabelPoint(options);
  }

  // 点-半固定水设施
  createHalfFixedWaterFacilityPoint(options: LabelPointOptions) {
    options.iconType = "点-半固定水设施";
    this.createLabelPoint(options);
  }
  // 点-喷淋系统
  createSprayWaterSystemPoint(options: LabelPointOptions) {
    options.iconType = "点-喷淋系统";
    this.createLabelPoint(options);
  }
  // 点-固定水炮
  createFixedWaterCannonPoint(options: LabelPointOptions) {
    options.iconType = "点-固定水炮";
    this.createLabelPoint(options);
  }
  // 点-天然水源取水
  createWaterSourcePoint(options: LabelPointOptions) {
    options.iconType = "点-天然水源取水";
    this.createLabelPoint(options);
  }
  // 点-室内消防栓
  createIndoorFireSprinklerPoint(options: LabelPointOptions) {
    options.iconType = "点-室内消防栓";
    this.createLabelPoint(options);
  }
  // 点-室外消防栓
  createOutdoorFireSprinklerPoint(options: LabelPointOptions) {
    options.iconType = "点-室外消防栓";
    this.createLabelPoint(options);
  }
  // 点-水泵接合器
  createWaterPumpConnectorPoint(options: LabelPointOptions) {
    options.iconType = "点-水泵接合器";
    this.createLabelPoint(options);
  }
  // 点-消防水池
  createFireWaterPoolPoint(options: LabelPointOptions) {
    options.iconType = "点-消防水池";
    this.createLabelPoint(options);
  }
  // 点-消防水箱
  createFireWaterBoxPoint(options: LabelPointOptions) {
    options.iconType = "点-消防水箱";
    this.createLabelPoint(options);
  }
  // 点-消防水鹤
  createFireWaterEelPoint(options: LabelPointOptions) {
    options.iconType = "点-消防水鹤";
    this.createLabelPoint(options);
  }
  // 点-消防泵房
  createFireWaterPumpRoomPoint(options: LabelPointOptions) {
    options.iconType = "点-消防泵房";
    this.createLabelPoint(options);
  }

  // 贝塞尔曲线平滑算法
  bezierSmooth(points: Array<any>, smoothness: number) {
    if (points.length < 3) return points;

    const smoothedPoints: Array<any> = [];
    const tension: number = smoothness / 10; // 张力系数，控制平滑程度

    // 添加起点
    smoothedPoints.push(points[0]);

    for (let i = 0; i < points.length - 1; i++) {
      const p0 = i > 0 ? points[i - 1] : points[i];
      const p1 = points[i];
      const p2 = points[i + 1];
      const p3 = i !== points.length - 2 ? points[i + 2] : p2;

      // 计算控制点
      const cp1x = p1.x + (p2.x - p0.x) * tension;
      const cp1y = p1.y + (p2.y - p0.y) * tension;
      const cp2x = p2.x - (p3.x - p1.x) * tension;
      const cp2y = p2.y - (p3.y - p1.y) * tension;

      // 使用二次贝塞尔曲线插入点
      for (let t = 0; t < 1; t += 0.1) {
        const t2 = t * t;
        const t3 = t2 * t;
        const mt = 1 - t;
        const mt2 = mt * mt;
        const mt3 = mt2 * mt;

        const x =
          mt3 * p1.x + 3 * mt2 * t * cp1x + 3 * mt * t2 * cp2x + t3 * p2.x;
        const y =
          mt3 * p1.y + 3 * mt2 * t * cp1y + 3 * mt * t2 * cp2y + t3 * p2.y;

        smoothedPoints.push(
          new Point({
            x: x,
            y: y,
            spatialReference: points[0].spatialReference,
          })
        );
      }
    }

    // 添加终点
    smoothedPoints.push(points[points.length - 1]);

    return smoothedPoints;
  }

  // 平滑折线函数
  smoothPolyline(polyline: PolyLine, smoothness = 5) {
    const points = polyline.paths[0].map(
      (coords) =>
        new Point({
          x: coords[0],
          y: coords[1],
          spatialReference: polyline.spatialReference,
        })
    );

    // 使用贝塞尔曲线平滑算法
    const smoothedPoints = this.bezierSmooth(points, smoothness);

    // 创建平滑后的折线几何对象
    return new PolyLine({
      paths: [smoothedPoints.map((point) => [point.x, point.y])],
      spatialReference: polyline.spatialReference,
    });
  }

  async createPointLine(options: PointLineOptions) {
    const {
      pointArray,
      iconType = "警车实际图",
      width = 40,
      height = 64,
      text = "",
      fontSize = 14,
      fontColor = "white",
      titleTip = tipPic,
    } = options;

    let textPointOptions: LabelPointOptions = {
      pointArray: [pointArray[0][0], pointArray[0][1]],
      text: text,
      iconType: iconType,
      width: width,
      height: height,
      fontSize: fontSize,
      fontColor: fontColor,
      titleTip: titleTip,
    };

    let { graphic, textGraphic } = await this.createTextPicturePoint(
      textPointOptions
    );

    let bottomLine = new PolyLine({
      paths: [pointArray],
    });

    let curseLine = this.smoothPolyline(bottomLine, 2);

    let bottomGraphiic = new Graphic({
      //将polyline加入到图层中
      geometry: curseLine,
      symbol: this.bottomLineSymbol,
    });

    this.addMany([bottomGraphiic, graphic, textGraphic]);
    this.pointLines.push({
      type: "point-line",
      textGraphic: textGraphic,
      graphic: graphic,
      bottomGraphic: bottomGraphiic,
      interval: null,
      dynamiceGraphic: null,
    });
  }

  // 警车-实际图
  createPolicemanPoint(options: PointLineOptions) {
    options.iconType = "警车实际图";
    this.createPointLine(options);
  }
  // 云梯车-实际图
  createElevatorCarPoint(options: PointLineOptions) {
    options.iconType = "云梯车-实际图";
    this.createPointLine(options);
  }
  // 医生-实际图
  createDoctorPoint(options: PointLineOptions) {
    options.iconType = "医生-实际图";
    this.createPointLine(options);
  }
  // 救护车实际图
  createAmbulancePoint(options: PointLineOptions) {
    options.iconType = "救护车实际图";
    this.createPointLine(options);
  }
  // 无人机实际图
  createUAVPoint(options: PointLineOptions) {
    options.iconType = "无人机实际图";
    this.createPointLine(options);
  }
  // 民警-实际图
  createPolicePoint(options: PointLineOptions) {
    options.iconType = "民警-实际图";
    this.createPointLine(options);
  }
  // 消防员-实际图
  createFirefighterPoint(options: PointLineOptions) {
    options.iconType = "消防员-实际图";
    this.createPointLine(options);
  }
  // 消防车-实际图
  createFiretruckPoint(options: PointLineOptions) {
    options.iconType = "消防车-实际图";
    this.createPointLine(options);
  }
  // 直升机-实际图
  createAirplanePoint(options: PointLineOptions) {
    options.iconType = "直升机-实际图";
    this.createPointLine(options);
  }
  // 船-实际图
  createShipPoint(options: PointLineOptions) {
    options.iconType = "船-实际图";
    this.createPointLine(options);
  }
  // 群众-实际图
  createCitizenPoint(options: PointLineOptions) {
    options.iconType = "群众-实际图";
    this.createPointLine(options);
  }

  /**
   *@Description: 计算线段与水平向东方向的方向角
   *@Parameters:
   *@MethodAuthor: yfyang
   *@Date: 2023-02-25 13:32:52
   */
  getAngle(last: any, lastSec: any) {
    var angle = 0;
    var thirdP = { x: 0, y: 0 };
    if (last.x > lastSec.x) {
      if (last.y == lastSec.y) {
        return angle;
      }
      thirdP.x = last.x;
      thirdP.y = lastSec.y;
    } else if (last.x < lastSec.x) {
      if (last.y == lastSec.y) {
        angle = 180;
        return angle;
      }
      thirdP.x = lastSec.x + lastSec.x - last.x;
      thirdP.y = lastSec.y;
    } else {
      if (last.y > lastSec.y) {
        angle = 90;
        return angle;
      } else if (last.y < lastSec.y) {
        angle = 270;
        return angle;
      }
    }
    let x1 = last.x - lastSec.x;
    let x2 = thirdP.x - lastSec.x;
    let y1 = last.y - lastSec.y;
    let y2 = thirdP.y - lastSec.y;
    const dot = x1 * x2 + y1 * y2;
    const det = x1 * y2 - y1 * x2;
    angle = (Math.atan2(det, dot) / Math.PI) * 180;
    return Math.round(angle + 360) % 360;
  }

  /**
   *@Description: 计算X轴方向图标偏移量
   *@Parameters:
   *@MethodAuthor: yfyang
   *@Date: 2023-02-25 13:45:27
   */
  caculateXOffset(angle: number) {
    const cir = ((Math.round(angle + 360) % 360) * Math.PI) / 180;
    return -3 * Math.sin(cir);
  }

  // 动画任务
  createDynamicLine(speed: number = 100, densifyDistance: number = 200) {
    this.pointLines.forEach(
      (item: {
        graphic: Graphic;
        bottomGraphic: Graphic;
        interval: any;
        dynamiceGraphic: Graphic | null;
      }) => {
        let markerGraphic = item.graphic;
        let bottomGraphic = item.bottomGraphic;

        let densifyLine: any = geometryEngine.geodesicDensify(
          bottomGraphic.geometry as PolyLine,
          densifyDistance,
          "meters"
        );

        let densifyPath = densifyLine.paths[0];

        // 定义起点和终点
        const dynamicLine = new PolyLine({
          paths: [
            [densifyPath[0][0], densifyPath[0][1]],
            [densifyPath[1][0], densifyPath[1][1]],
          ],
        });

        const dynamicGraphic = new Graphic({
          geometry: dynamicLine,
          symbol: this.dynamicLineSymbol,
        });
        this.add(dynamicGraphic);
        item.dynamiceGraphic = dynamicGraphic;

        this.remove(markerGraphic);
        this.add(markerGraphic);

        let index = 1;
        let that = this;
        const interval = setInterval(() => {
          index++;

          if (index >= densifyPath.length - 2) {
            index = 2;
          }

          let currentPath = densifyPath.slice(0, index);

          dynamicGraphic.geometry = new PolyLine({
            paths: currentPath,
          });

          let lastX = currentPath[index - 1][0];
          let lastY = currentPath[index - 1][1];
          let lastSecX = currentPath[index - 2][0];
          let lastSecY = currentPath[index - 2][1];

          let angle = that.getAngle(
            { x: lastX, y: lastY },
            { x: lastSecX, y: lastSecY }
          );

          if (angle > 90 && angle < 270) {
            if ((markerGraphic.symbol as PictureMarkerSymbol).width > 0) {
              (markerGraphic.symbol as PictureMarkerSymbol).width = -(
                markerGraphic.symbol as PictureMarkerSymbol
              ).width;
            }
          } else {
            if ((markerGraphic.symbol as PictureMarkerSymbol).width < 0) {
              (markerGraphic.symbol as PictureMarkerSymbol).width = -(
                markerGraphic.symbol as PictureMarkerSymbol
              ).width;
            }
          }

          markerGraphic.geometry = new Point({
            x: lastX,
            y: lastY,
          });
        }, speed);

        item.interval = interval;
      }
    );
  }

  // 停止动画
  stopDynamicLine() {
    this.pointLines.forEach(
      (item: {
        graphic: Graphic;
        bottomGraphic: Graphic;
        interval: any;
        dynamiceGraphic: Graphic | null;
      }) => {
        clearInterval(item.interval);
        if (item.dynamiceGraphic) {
          this.remove(item.dynamiceGraphic);
          item.dynamiceGraphic = null;
        }
      }
    );
  }
}